/* SPDX-License-Identifier: GTDGmbH */
/* Copyright 2020-2021 by GTD GmbH. */

/**
 *
 * This family of functions fuses the multiplication of :math:`x` and :math:`y`
 * with the addition of :math:`z`.
 *
 * Synopsis
 * ========
 *
 * .. code-block:: c
 *
 *     #include <math.h>
 *     float fmaf(float x, float y, float z);
 *     double fma(double x, double y, double z);
 *     long double fmal(long double x, long double y, long double z);
 *
 * Description
 * ===========
 *
 * ``fma`` computes the multiplication of :math:`x` and :math:`y` with the
 * addition of :math:`z`.
 *
 * Mathematical Function
 * =====================
 *
 * .. math::
 *
 *    fma(x, y, z) = x \cdot y + z
 *
 * Returns
 * =======
 *
 * ``fma`` returns the multiplication of :math:`x` and :math:`y` with the
 * addition of :math:`z`.
 *
 * Exceptions
 * ==========
 *
 * Raise ``invalid operation`` exception when either :math:`x` or :math:`y` is
 * zero and the other is infinity (regardless of the value in :math:`z`), or
 * when :math:`x \cdot y` is infinity and :math:`z` is infinity with the
 * opposite sign.
 *
 * Raise ``overflow`` exception if the result overflows.
 *
 * .. May raise ``underflow`` exception.
 *
 * Output map
 * ==========
 *
 * +--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
 * | :math:`x \cdot y`        | x                                                                                                                                                                                          |
 * +--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
 * | y                        | :math:`-Inf`             | :math:`<0`               | :math:`-0`               | :math:`+0`               | :math:`>0`               | :math:`+Inf`             | :math:`NaN`              |
 * +==========================+==========================+==========================+==========================+==========================+==========================+==========================+==========================+
 * | :math:`-Inf`             | :math:`+Inf`             | :math:`+Inf`             | :math:`qNaN`                                        | :math:`+Inf`             | :math:`+Inf`             | :math:`qNaN`             |
 * +--------------------------+                          +--------------------------+--------------------------+--------------------------+--------------------------+                          +                          +
 * | :math:`<0`               |                          | :math:`x \cdot y`        | :math:`+0`               | :math:`-0`               | :math:`x \cdot y`        |                          |                          |
 * +--------------------------+--------------------------+--------------------------+                          +                          +--------------------------+--------------------------+                          +
 * | :math:`-0`               | :math:`qNaN`             | :math:`+0`               |                          |                          | :math:`-0`               | :math:`qNaN`             |                          |
 * +--------------------------+                          +--------------------------+--------------------------+--------------------------+--------------------------+                          +                          +
 * | :math:`+0`               |                          | :math:`-0`               | :math:`-0`               | :math:`+0`               | :math:`+0`               |                          |                          |
 * +--------------------------+--------------------------+--------------------------+                          +                          +--------------------------+--------------------------+                          +
 * | :math:`>0`               |                          | :math:`x \cdot y`        |                          |                          | :math:`x \cdot y`        |                          |                          |
 * +--------------------------+                          +--------------------------+--------------------------+--------------------------+--------------------------+                          +                          +
 * | :math:`+Inf`             | :math:`-Inf`             | :math:`-Inf`             | :math:`qNaN`                                        | :math:`-Inf`             | :math:`-Inf`             |                          |
 * +--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+                          +
 * | :math:`NaN`              | :math:`qNaN`                                                                                                                                                    |                          |
 * +--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
 *
 * +--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
 * | fma(x,y,z)               | z                                                                                                                                                                                          |
 * +--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
 * | :math:`x \cdot y`        | :math:`-Inf`             | :math:`<0`               | :math:`-0`               | :math:`+0`               | :math:`>0`               | :math:`+Inf`             | :math:`NaN`              |
 * +==========================+==========================+==========================+==========================+==========================+==========================+==========================+==========================+
 * | :math:`-Inf`             | :math:`-Inf`             | :math:`-Inf`                                                                                              | :math:`qNaN`             | :math:`qNaN`             |
 * +--------------------------+                          +--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+                          +
 * | :math:`<0`               |                          | :math:`x \cdot y + z`    | :math:`x \cdot y`                                   | :math:`x \cdot y + z`    | :math:`+Inf`             |                          |
 * +--------------------------+                          +--------------------------+--------------------------+--------------------------+--------------------------+                          +                          +
 * | :math:`-0`               |                          | :math:`z`                | :math:`-0`               | :math:`+0`               | :math:`z`                |                          |                          |
 * +--------------------------+                          +                          +--------------------------+--------------------------+                          +                          +                          +
 * | :math:`+0`               |                          |                          | :math:`+0`               | :math:`+0`               |                          |                          |                          |
 * +--------------------------+                          +--------------------------+--------------------------+--------------------------+--------------------------+                          +                          +
 * | :math:`>0`               |                          | :math:`x \cdot y + z`    | :math:`x \cdot y`                                   | :math:`x \cdot y + z`    |                          |                          |
 * +--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+                          +                          +
 * | :math:`+Inf`             | :math:`qNaN`             | :math:`+Inf`                                                                                              |                          |                          |
 * +--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
 * | :math:`NaN`              | :math:`qNaN`                                                                                                                                                                               |
 * +--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
 *
 *///

#include <math.h>

#ifndef __LIBMCS_DOUBLE_IS_32BITS

double fma(double x, double y, double z)
{
#ifdef __LIBMCS_FPU_DAZ
    x *= __volatile_one;
    y *= __volatile_one;
    z *= __volatile_one;
#endif /* defined(__LIBMCS_FPU_DAZ) */

    return x * y + z;
}

#ifdef __LIBMCS_LONG_DOUBLE_IS_64BITS

long double fmal(long double x, long double y, long double z)
{
    return (long double) fma((double) x, (double) y, (double) z);
}

#endif /* #ifdef __LIBMCS_LONG_DOUBLE_IS_64BITS */
#endif /* #ifndef __LIBMCS_DOUBLE_IS_32BITS */
